home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / GameKit / gamekit-1 / DirtPile.m < prev    next >
Text File  |  1995-06-12  |  7KB  |  238 lines

  1.  
  2. // This object tracks the dirty rectangle of the frame buffer.
  3. // If two rects overlap, they are coalesced.  If they don't
  4. // overlap, they are kept separate.  This way, when a redraw
  5. // is done, we have two flushes that are small rather than one
  6. // large flush.  (The large flush would draw *lots* of unnecessary
  7. // pixels!)
  8.  
  9. #import <gamekit/gamekit.h>
  10.  
  11. #import <appkit/appkit.h>
  12. #import <dpsclient/dpsNeXT.h>
  13. #import <stdio.h>
  14. #import <math.h>
  15. #import <sys/param.h>  // MAX()  MIN()  macros
  16.  
  17. @interface DirtPile(private)
  18. - _coalesce;
  19. @end
  20.  
  21. // It might be faster to call NXRectClipList() and then
  22. // composite the whole buffer.  I'll have to test this. *****
  23. // leaving this undefined flushes each rect explicitly
  24. // Note that this only changes the default; you can set it
  25. // yourself via the -setManyFlushes: method.
  26. // #define DIRTPILE_USECLIPLIST // use the clipping
  27.  
  28. // function to coalesce two rectangles if they overlap.
  29. // Returns NO if they don't, returns YES if they do and
  30. // leaves the union rect in *r1.  I suppose that I could
  31. // have used NXIntersectsRect() and NXUnionRect(), but
  32. // this is just as easy.  This should probably be inline. *****
  33. BOOL coalesce(NXRect *r1, NXRect *r2, double percent)
  34. {
  35.     NXRect r3, r4; double overlap;
  36.     
  37.     // see if the two rects intersect.  If they don't, return NO.
  38.     // Saving the overhead of a function call to NXIntersectsRect()...
  39.     if (((NX_X(r1) + NX_WIDTH(r1)) < NX_X(r2)) ||
  40.         ((NX_X(r2) + NX_WIDTH(r2)) < NX_X(r1)) ||
  41.         ((NX_Y(r1) + NX_HEIGHT(r1)) < NX_Y(r2)) ||
  42.         ((NX_Y(r2) + NX_HEIGHT(r2)) < NX_Y(r1))) return NO;
  43.     
  44.     // take the union of the rects; the new, larger rect is in r3
  45.     NX_X(&r3) = MIN(NX_X(r1), NX_X(r2));
  46.     NX_Y(&r3) = MIN(NX_Y(r1), NX_Y(r2));
  47.     NX_WIDTH(&r3) = MAX(NX_MAXX(r1), NX_MAXX(r2)) - NX_X(&r3);
  48.     NX_HEIGHT(&r3) = MAX(NX_MAXY(r1), NX_MAXY(r2)) - NX_Y(&r3);
  49.  
  50.     // get the inersection of the rects in r4
  51.     NX_X(&r4) = MAX(NX_X(r1), NX_X(r2));
  52.     NX_Y(&r4) = MAX(NX_Y(r1), NX_Y(r2));
  53.     NX_WIDTH(&r4) = MIN(NX_MAXX(r1), NX_MAXX(r2)) - NX_X(&r4);
  54.     NX_HEIGHT(&r4) = MIN(NX_MAXY(r1), NX_MAXY(r2)) - NX_Y(&r4);
  55.  
  56.     // how much overlap??  If not enough overlap, don't coalesce
  57.     // this amounts to %of redraw == dirty area / coalesced area;
  58.     // if we will end up adding too much undirty area, we won't coalesce
  59.     // dirty area = dirty area 1 + dirty area 2 - union area of 1&2
  60.     // (subtract union area so it isn't counted twice)
  61.     // for speed we could ignore the union area, in which case the
  62.     // percent param ranges from (.50 == always) to (2.0 == never)
  63.     overlap = (((NX_WIDTH(r1) * NX_HEIGHT(r1)) +
  64.                 (NX_WIDTH(r2) * NX_HEIGHT(r2))) -
  65.                 (NX_WIDTH(&r4) * NX_HEIGHT(&r4))) /
  66.                 (NX_WIDTH(&r3) * NX_HEIGHT(&r3));
  67.     if (overlap < percent) return NO;
  68.     *r1 = r3;
  69.     return YES;    // tell them we coalesced it.
  70. }
  71.  
  72.  
  73. @implementation DirtPile
  74.  
  75. - init        // initialize the instance
  76. {
  77.     [super init];
  78.     // reset instance variables.
  79.     maxRects = MAX_RECTS;
  80.     numRects = 0; allDirty = NO;
  81.     coalesceFrequency = MAXINT; // only do it on a flush
  82.     rectsAdded = 0;
  83. #ifdef DIRTPILE_USECLIPLIST    // define it or not to change the default
  84.     manyFlushes = NO;
  85.     percentOverlap = 0.95;    // almost never coalesce overlapping rects
  86.                             // since postscript will do this for us
  87. #else
  88.     manyFlushes = YES;
  89.     percentOverlap = 0.5;    // always coalesce overlapping rects to
  90.                             // reduce the number of flushes we send
  91. #endif
  92.     return self;
  93. }
  94.  
  95.  
  96. - addRegion:(float)x :(float)y :(float)w :(float)h    // add a dirty rect
  97. {
  98.     register NXRect *r;
  99.     if (allDirty) return self;
  100.     // make sure we have enough room.  Complain if we don't.  (Shouldn't
  101.     // ever get the printf(), though! )
  102.     if (numRects >= maxRects) {
  103.         fprintf(stderr, "Need more rects allocated in DirtPile!\n");
  104.         return self;
  105.     }
  106.     
  107.     // copy coords into the rect list
  108.     r = &rectList[numRects];
  109.     NX_X(r) = x; NX_Y(r) = y;
  110.     NX_WIDTH(r) = w; NX_HEIGHT(r) = h;
  111.     
  112.     // update number of rects in list
  113.     numRects++;
  114.     
  115.     // do a coalesce?
  116.     if (++rectsAdded >= coalesceFrequency) {
  117.         rectsAdded = 0;
  118.         [self _coalesce];
  119.     }
  120.     return self;
  121. }
  122.  
  123. - addRegion:(const NXRect *)rect    // add a dirty rect
  124. {
  125.     return [self addRegion:NX_X(rect) :NX_Y(rect)
  126.         :NX_WIDTH(rect) :NX_HEIGHT(rect)];
  127. }
  128.  
  129. - fullRedraw:sender :buffer    // assumed to be a view
  130. { // ***** inefficient; just use composite:toPoint and remove sender arg
  131.     // (Of course, that change requires that the lockedView/buffer have a
  132.     // proper clipping path set!)
  133.     NXRect bounds = {{0.0, 0.0}, {0.0, 0.0}};
  134.     
  135.     [sender getBounds:&bounds];
  136.     [buffer composite:NX_COPY fromRect:&bounds
  137.             toPoint:&(bounds.origin)];
  138.     numRects = 0; allDirty = NO; rectsAdded = 0;
  139.     return self;
  140. }
  141.  
  142. - _coalesce
  143. {    // I need to come up with a faster way to do this; it
  144.     // tends to be an n^3 algorithm!  *****
  145.     register int i, j;
  146.     BOOL changed = NO;
  147.     register NXRect *r1, *r2;
  148.     r1 = &rectList[0];    // unneeded; silences a compiler warning
  149.     do {
  150.         changed = NO;
  151.         for (i=0; i<(numRects-1); i++) {
  152.             r1 = &rectList[i];
  153.             for (j=i+1; j<numRects; j++) {
  154.                 r2 = &rectList[j];
  155.                 if (coalesce(r1, r2, percentOverlap)) {
  156.                     changed = YES;
  157.                     numRects--;
  158.                     for (; j<numRects; j++) {
  159.                         *r2 = *(r2+1);
  160.                         r2++;
  161.                     }
  162.                 }
  163.             }
  164.         }
  165.     } while (changed);
  166.     return self;
  167. }
  168.  
  169. // Flush dirty parts of the buffer to the currently focused view.
  170. // The view should be in a retained window for this to actually
  171. // work as it is supposed to do.
  172. - doRedraw:buffer
  173. {
  174.     NXPoint zero = { 0.0, 0.0 };
  175.     register int i;
  176.  
  177.     rectsAdded = 0;
  178.     if (allDirty) {
  179.         allDirty = NO;
  180.         [buffer composite:NX_COPY toPoint:&zero];
  181.     }
  182.     
  183.     // Leave if nothing here to flush.
  184.     if (!numRects) return self;
  185.     [self _coalesce];
  186.     
  187.     // flush dirty rects from buffer onto the screen
  188.     if (!buffer) { // use internal color if no buffer
  189.         NXSetColor(noBufferColor);
  190.         NXRectFillList(rectList, numRects);
  191.     } else {
  192.         // do the buffer flush.  Two ways to do it:  clip and then
  193.         // flush all, or flush each rect explicitly.
  194.         if (manyFlushes) {
  195.             for (i=0; i<numRects; i++) {
  196.                 [buffer composite:NX_COPY fromRect:&rectList[i]
  197.                         toPoint:&rectList[i].origin];
  198.             }
  199.         } else {
  200.             NXRectClipList(rectList, numRects);
  201.             [buffer composite:NX_COPY toPoint:&zero];
  202.         }
  203.     }
  204.     // if using a buffered window instead of retained, you
  205.     // need to flush the window here.
  206.     // reset dirty list, since it's all clean now
  207.     numRects = 0;
  208.     return self;
  209. }
  210.  
  211. - setAllDirty { numRects = 0; allDirty = YES; return self; }
  212.  
  213. - sendDirtTo:aDirtPile        // add all our dirty rects to aDirtPile
  214. {
  215.     register int i;
  216.     if (!numRects) return self; // no work to do 'cause we're clean
  217.     [self _coalesce]; // do this efficiently...
  218.     for (i=0; i<numRects; i++) {
  219.         [aDirtPile addRegion:&rectList[i]];
  220.     }
  221.     return self;
  222. }
  223.  
  224. - setNoBufferColor:(NXColor)aColor // set color to draw if no buffer
  225. {
  226.     noBufferColor = aColor;
  227.     return self;
  228. }
  229.  
  230. - (double)percentDirtyForCoalesce { return percentOverlap; }
  231. - setPercentDirtyForCoalesce:(double)val { percentOverlap = val; return self; }
  232. - (int)coalesceFrequency { return coalesceFrequency; }
  233. - setCoalesceFrequency:(int)val { coalesceFrequency = val; return self; }
  234. - (BOOL)manyFlushes { return manyFlushes; }
  235. - setManyFlushes:(BOOL)flag { manyFlushes = flag; return self; }
  236.  
  237. @end
  238.